package com.study.ThreadDemo;

import com.study.Thread.SmallTool;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class CompletableFutureDemo {
    public static void main(String[] args) {
        demo3();
    }
    public static void demo3(){
        List<User> users = Arrays.asList(new User("张三", 20, "男"),
                new User("李四", 10, "男"),
                new User("王五", 19, "男"),
                new User("赵六", 21, "男"),
                new User("钱七", 22, "男"),
                new User("元宝", 22, "女")
        );
        //批量保存用户
        List<CompletableFuture<User>> resultList = users.stream().map(user -> CompletableFuture.supplyAsync(() -> {
            //需要一个供给型参数，使用内部线程池异步执行
            //保存到数据库
            SmallTool.printTimeAndThread("设置用户id");
            user.setId(UUID.randomUUID().toString());
            return user;
        })).collect(Collectors.toList());
        //这个时候，得到的resultList还没有结果，因为CompletableFuture是内部使用线程池进行执行的
        //把上一步所有的future遍历，然后对每一个的结果进行处理，这里模拟入成年库
        List<CompletableFuture<User>> collect = resultList.stream().map(resultFuture -> resultFuture.thenApplyAsync(res -> {
            //res是上一步中入库的user结果对象
            //这里模拟把年龄大于19的user保存到成年库中
            if (res.getAge() >= 18) {
                SmallTool.printTimeAndThread("过滤未成年人");
                return res;
            }
            return null;
        })).collect(Collectors.toList());
        //获取所有入库结果
        List<User> allResult = resultList.stream().map(CompletableFuture::join).filter(Objects::nonNull).collect(Collectors.toList());
        System.out.println("全部人员"+allResult);
        List<User> adultResult=collect.stream().map(CompletableFuture::join).filter(Objects::nonNull).collect(Collectors.toList());
        System.out.println("成年人"+adultResult);

    }

    /**
     * 底层默认使用ForkJoinPool
     * 事实上，如果每个操作都很简单的话（比如上面的例子按照id去查找）没有必要使用这种多线程异步的方式
     * 因为创建线程也需要时间，还不如直接同步执行来的快
     * 事实证明 只有当每一个操作很复杂需要花费很长的时间（比如，调用多个其他的系统的接口）
     * 比如，商品详情页面这种需要需要从多个系统中查数据显示的的时候用CompletableFuture才合适，不然区别真的不大，还不如顺序同步执行
     * 1.异步多线程入库，并获取都有入库结果
     */
    public static void demo1(){
        List<String> users = Arrays.asList("张三", "李四", "王五", "马六");
        //批量保存用户
        List<CompletableFuture<HashMap<String, String>>> resultList = users.stream().map(user -> CompletableFuture.supplyAsync(() -> {
            //这里需要一个供给型参数，使用内部线程池异步执行
            //保存到数据库
            HashMap<String, String> result = new HashMap<>();
            result.put("name", user);
            result.put("id", UUID.randomUUID().toString());
            return result;
        })).collect(Collectors.toList());
        //这个时候，得到的resultList还没有结果，因为CompletableFuture是内部使用线程池执行的
        //等待所有完成
        //CompletableFuture.allof(resultList.Array(new CompletableFuture[0])).join();
        //等待所有完成并获取结果
        List<HashMap<String, String>> realResultList = resultList.stream().map(CompletableFuture::join).collect(Collectors.toList());
        System.out.println(realResultList);
    }
    /**
     * 2.异步多线程入库，获取每个结果后再操作，再获取所有入库结果
     * supplyAsync 首次操作，异步有入参有结果
     * thenApplyAsync 后续操作，异步有入参有结果
     * 连续操作
     */
    public static void demo2() {
        List<User> users = Arrays.asList
                (new User("张三", 10, "男"),
                        new User("李四", 20, "男"),
                        new User("王五", 30, "男"),
                        new User("马六", 40, "女"));
        //批量保存用户
        List<CompletableFuture<User>> resultList = users.stream().map(user -> CompletableFuture.supplyAsync(
                () -> {
                    //这里需要一个供给型参数，使用内部线程池异步执行
                    //保存到数据库
                    user.setId(UUID.randomUUID().toString());
                    return user;
                }
        )).collect(Collectors.toList());
        //这个时候，得到的resultList还没有结果，因为CompletableFuture是内部使用线程池进行执行的
        //把上一步所有的future遍历，然后对每一个的结果进行处理，这里模拟入成年库
        List<CompletableFuture<User>> collect = resultList.stream().map(resultFuture -> resultFuture.thenApplyAsync(res -> {
            //res是上一步中入库的User结果对象
            //这里模拟把年龄大于18的user保存到成年库
            if (res.getAge() >= 18) {
                return res;
            }
            return null;
        })).collect(Collectors.toList());
        //获取所有入库结果
        List<User> allResult = resultList.stream().map(CompletableFuture::join).filter(Objects::nonNull).collect(Collectors.toList());
        System.out.println(allResult);
        //获取成年库入库结果
        List<User> adultResult = collect.stream().map(CompletableFuture::join).filter(Objects::nonNull).collect(Collectors.toList());
        System.out.println(adultResult);//没有小于18岁的

    }
}
